/**
 * Orthanc - A Lightweight, RESTful DICOM Store
 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
 * Department, University Hospital of Liege, Belgium
 * Copyright (C) 2017-2023 Osimis S.A., Belgium
 * Copyright (C) 2024-2024 Orthanc Team SRL, Belgium
 * Copyright (C) 2021-2024 Sebastien Jodogne, ICTEAM UCLouvain, Belgium
 *
 * This program is free software: you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation, either version 3 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this program. If not, see
 * <http://www.gnu.org/licenses/>.
 **/


#include "PrecompiledHeaders.h"
#include "Enumerations.h"

#include "Logging.h"
#include "MultiThreading/Mutex.h"
#include "OrthancException.h"
#include "Toolbox.h"

#include <string.h>
#include <cassert>
#include <boost/algorithm/string/replace.hpp>

namespace Orthanc
{
  // This function is autogenerated by the script
  // "Resources/CodeGeneration/GenerateErrorCodes.py"
  const char* EnumerationToString(ErrorCode error)
  {
    switch (error)
    {
      case ErrorCode_InternalError:
        return "Internal error";

      case ErrorCode_Success:
        return "Success";

      case ErrorCode_Plugin:
        return "Error encountered within the plugin engine";

      case ErrorCode_NotImplemented:
        return "Not implemented yet";

      case ErrorCode_ParameterOutOfRange:
        return "Parameter out of range";

      case ErrorCode_NotEnoughMemory:
        return "The server hosting Orthanc is running out of memory";

      case ErrorCode_BadParameterType:
        return "Bad type for a parameter";

      case ErrorCode_BadSequenceOfCalls:
        return "Bad sequence of calls";

      case ErrorCode_InexistentItem:
        return "Accessing an inexistent item";

      case ErrorCode_BadRequest:
        return "Bad request";

      case ErrorCode_NetworkProtocol:
        return "Error in the network protocol";

      case ErrorCode_SystemCommand:
        return "Error while calling a system command";

      case ErrorCode_Database:
        return "Error with the database engine";

      case ErrorCode_UriSyntax:
        return "Badly formatted URI";

      case ErrorCode_InexistentFile:
        return "Inexistent file";

      case ErrorCode_CannotWriteFile:
        return "Cannot write to file";

      case ErrorCode_BadFileFormat:
        return "Bad file format";

      case ErrorCode_Timeout:
        return "Timeout";

      case ErrorCode_UnknownResource:
        return "Unknown resource";

      case ErrorCode_IncompatibleDatabaseVersion:
        return "Incompatible version of the database";

      case ErrorCode_FullStorage:
        return "The file storage is full";

      case ErrorCode_CorruptedFile:
        return "Corrupted file (e.g. inconsistent MD5 hash)";

      case ErrorCode_InexistentTag:
        return "Inexistent tag";

      case ErrorCode_ReadOnly:
        return "Cannot modify a read-only data structure";

      case ErrorCode_IncompatibleImageFormat:
        return "Incompatible format of the images";

      case ErrorCode_IncompatibleImageSize:
        return "Incompatible size of the images";

      case ErrorCode_SharedLibrary:
        return "Error while using a shared library (plugin)";

      case ErrorCode_UnknownPluginService:
        return "Plugin invoking an unknown service";

      case ErrorCode_UnknownDicomTag:
        return "Unknown DICOM tag";

      case ErrorCode_BadJson:
        return "Cannot parse a JSON document";

      case ErrorCode_Unauthorized:
        return "Bad credentials were provided to an HTTP request";

      case ErrorCode_BadFont:
        return "Badly formatted font file";

      case ErrorCode_DatabasePlugin:
        return "The plugin implementing a custom database back-end does not fulfill the proper interface";

      case ErrorCode_StorageAreaPlugin:
        return "Error in the plugin implementing a custom storage area";

      case ErrorCode_EmptyRequest:
        return "The request is empty";

      case ErrorCode_NotAcceptable:
        return "Cannot send a response which is acceptable according to the Accept HTTP header";

      case ErrorCode_NullPointer:
        return "Cannot handle a NULL pointer";

      case ErrorCode_DatabaseUnavailable:
        return "The database is currently not available (probably a transient situation)";

      case ErrorCode_CanceledJob:
        return "This job was canceled";

      case ErrorCode_BadGeometry:
        return "Geometry error encountered in Stone";

      case ErrorCode_SslInitialization:
        return "Cannot initialize SSL encryption, check out your certificates";

      case ErrorCode_DiscontinuedAbi:
        return "Calling a function that has been removed from the Orthanc Framework";

      case ErrorCode_BadRange:
        return "Incorrect range request";

      case ErrorCode_DatabaseCannotSerialize:
        return "Database could not serialize access due to concurrent update, the transaction should be retried";

      case ErrorCode_Revision:
        return "A bad revision number was provided, which might indicate conflict between multiple writers";

      case ErrorCode_MainDicomTagsMultiplyDefined:
        return "A main DICOM Tag has been defined multiple times for the same resource level";

      case ErrorCode_ForbiddenAccess:
        return "Access to a resource is forbidden";

      case ErrorCode_DuplicateResource:
        return "Duplicate resource";

      case ErrorCode_SQLiteNotOpened:
        return "SQLite: The database is not opened";

      case ErrorCode_SQLiteAlreadyOpened:
        return "SQLite: Connection is already open";

      case ErrorCode_SQLiteCannotOpen:
        return "SQLite: Unable to open the database";

      case ErrorCode_SQLiteStatementAlreadyUsed:
        return "SQLite: This cached statement is already being referred to";

      case ErrorCode_SQLiteExecute:
        return "SQLite: Cannot execute a command";

      case ErrorCode_SQLiteRollbackWithoutTransaction:
        return "SQLite: Rolling back a nonexistent transaction (have you called Begin()?)";

      case ErrorCode_SQLiteCommitWithoutTransaction:
        return "SQLite: Committing a nonexistent transaction";

      case ErrorCode_SQLiteRegisterFunction:
        return "SQLite: Unable to register a function";

      case ErrorCode_SQLiteFlush:
        return "SQLite: Unable to flush the database";

      case ErrorCode_SQLiteCannotRun:
        return "SQLite: Cannot run a cached statement";

      case ErrorCode_SQLiteCannotStep:
        return "SQLite: Cannot step over a cached statement";

      case ErrorCode_SQLiteBindOutOfRange:
        return "SQLite: Bing a value while out of range (serious error)";

      case ErrorCode_SQLitePrepareStatement:
        return "SQLite: Cannot prepare a cached statement";

      case ErrorCode_SQLiteTransactionAlreadyStarted:
        return "SQLite: Beginning the same transaction twice";

      case ErrorCode_SQLiteTransactionCommit:
        return "SQLite: Failure when committing the transaction";

      case ErrorCode_SQLiteTransactionBegin:
        return "SQLite: Cannot start a transaction";

      case ErrorCode_DirectoryOverFile:
        return "The directory to be created is already occupied by a regular file";

      case ErrorCode_FileStorageCannotWrite:
        return "Unable to create a subdirectory or a file in the file storage";

      case ErrorCode_DirectoryExpected:
        return "The specified path does not point to a directory";

      case ErrorCode_HttpPortInUse:
        return "The TCP port of the HTTP server is privileged or already in use";

      case ErrorCode_DicomPortInUse:
        return "The TCP port of the DICOM server is privileged or already in use";

      case ErrorCode_BadHttpStatusInRest:
        return "This HTTP status is not allowed in a REST API";

      case ErrorCode_RegularFileExpected:
        return "The specified path does not point to a regular file";

      case ErrorCode_PathToExecutable:
        return "Unable to get the path to the executable";

      case ErrorCode_MakeDirectory:
        return "Cannot create a directory";

      case ErrorCode_BadApplicationEntityTitle:
        return "An application entity title (AET) cannot be empty or be longer than 16 characters";

      case ErrorCode_NoCFindHandler:
        return "No request handler factory for DICOM C-FIND SCP";

      case ErrorCode_NoCMoveHandler:
        return "No request handler factory for DICOM C-MOVE SCP";

      case ErrorCode_NoCStoreHandler:
        return "No request handler factory for DICOM C-STORE SCP";

      case ErrorCode_NoApplicationEntityFilter:
        return "No application entity filter";

      case ErrorCode_NoSopClassOrInstance:
        return "DicomUserConnection: Unable to find the SOP class and instance";

      case ErrorCode_NoPresentationContext:
        return "DicomUserConnection: No acceptable presentation context for modality";

      case ErrorCode_DicomFindUnavailable:
        return "DicomUserConnection: The C-FIND command is not supported by the remote SCP";

      case ErrorCode_DicomMoveUnavailable:
        return "DicomUserConnection: The C-MOVE command is not supported by the remote SCP";

      case ErrorCode_CannotStoreInstance:
        return "Cannot store an instance";

      case ErrorCode_CreateDicomNotString:
        return "Only string values are supported when creating DICOM instances";

      case ErrorCode_CreateDicomOverrideTag:
        return "Trying to override a value inherited from a parent module";

      case ErrorCode_CreateDicomUseContent:
        return "Use \"Content\" to inject an image into a new DICOM instance";

      case ErrorCode_CreateDicomNoPayload:
        return "No payload is present for one instance in the series";

      case ErrorCode_CreateDicomUseDataUriScheme:
        return "The payload of the DICOM instance must be specified according to Data URI scheme";

      case ErrorCode_CreateDicomBadParent:
        return "Trying to attach a new DICOM instance to an inexistent resource";

      case ErrorCode_CreateDicomParentIsInstance:
        return "Trying to attach a new DICOM instance to an instance (must be a series, study or patient)";

      case ErrorCode_CreateDicomParentEncoding:
        return "Unable to get the encoding of the parent resource";

      case ErrorCode_UnknownModality:
        return "Unknown modality";

      case ErrorCode_BadJobOrdering:
        return "Bad ordering of filters in a job";

      case ErrorCode_JsonToLuaTable:
        return "Cannot convert the given JSON object to a Lua table";

      case ErrorCode_CannotCreateLua:
        return "Cannot create the Lua context";

      case ErrorCode_CannotExecuteLua:
        return "Cannot execute a Lua command";

      case ErrorCode_LuaAlreadyExecuted:
        return "Arguments cannot be pushed after the Lua function is executed";

      case ErrorCode_LuaBadOutput:
        return "The Lua function does not give the expected number of outputs";

      case ErrorCode_NotLuaPredicate:
        return "The Lua function is not a predicate (only true/false outputs allowed)";

      case ErrorCode_LuaReturnsNoString:
        return "The Lua function does not return a string";

      case ErrorCode_StorageAreaAlreadyRegistered:
        return "Another plugin has already registered a custom storage area";

      case ErrorCode_DatabaseBackendAlreadyRegistered:
        return "Another plugin has already registered a custom database back-end";

      case ErrorCode_DatabaseNotInitialized:
        return "Plugin trying to call the database during its initialization";

      case ErrorCode_SslDisabled:
        return "Orthanc has been built without SSL support";

      case ErrorCode_CannotOrderSlices:
        return "Unable to order the slices of the series";

      case ErrorCode_NoWorklistHandler:
        return "No request handler factory for DICOM C-Find Modality SCP";

      case ErrorCode_AlreadyExistingTag:
        return "Cannot override the value of a tag that already exists";

      case ErrorCode_NoStorageCommitmentHandler:
        return "No request handler factory for DICOM N-ACTION SCP (storage commitment)";

      case ErrorCode_NoCGetHandler:
        return "No request handler factory for DICOM C-GET SCP";

      case ErrorCode_UnsupportedMediaType:
        return "Unsupported media type";

      default:
        if (error >= ErrorCode_START_PLUGINS)
        {
          return "Error encountered within some plugin";
        }
        else
        {
          return "Unknown error code";
        }
    }
  }


  const char* EnumerationToString(HttpMethod method)
  {
    switch (method)
    {
      case HttpMethod_Get:
        return "GET";

      case HttpMethod_Post:
        return "POST";

      case HttpMethod_Delete:
        return "DELETE";

      case HttpMethod_Put:
        return "PUT";

      default:
        return "?";
    }
  }


  const char* EnumerationToString(HttpStatus status)
  {
    switch (status)
    {
    case HttpStatus_100_Continue:
      return "Continue";

    case HttpStatus_101_SwitchingProtocols:
      return "Switching Protocols";

    case HttpStatus_102_Processing:
      return "Processing";

    case HttpStatus_200_Ok:
      return "OK";

    case HttpStatus_201_Created:
      return "Created";

    case HttpStatus_202_Accepted:
      return "Accepted";

    case HttpStatus_203_NonAuthoritativeInformation:
      return "Non-Authoritative Information";

    case HttpStatus_204_NoContent:
      return "No Content";

    case HttpStatus_205_ResetContent:
      return "Reset Content";

    case HttpStatus_206_PartialContent:
      return "Partial Content";

    case HttpStatus_207_MultiStatus:
      return "Multi-Status";

    case HttpStatus_208_AlreadyReported:
      return "Already Reported";

    case HttpStatus_226_IMUsed:
      return "IM Used";

    case HttpStatus_300_MultipleChoices:
      return "Multiple Choices";

    case HttpStatus_301_MovedPermanently:
      return "Moved Permanently";

    case HttpStatus_302_Found:
      return "Found";

    case HttpStatus_303_SeeOther:
      return "See Other";

    case HttpStatus_304_NotModified:
      return "Not Modified";

    case HttpStatus_305_UseProxy:
      return "Use Proxy";

    case HttpStatus_307_TemporaryRedirect:
      return "Temporary Redirect";

    case HttpStatus_400_BadRequest:
      return "Bad Request";

    case HttpStatus_401_Unauthorized:
      return "Unauthorized";

    case HttpStatus_402_PaymentRequired:
      return "Payment Required";

    case HttpStatus_403_Forbidden:
      return "Forbidden";

    case HttpStatus_404_NotFound:
      return "Not Found";

    case HttpStatus_405_MethodNotAllowed:
      return "Method Not Allowed";

    case HttpStatus_406_NotAcceptable:
      return "Not Acceptable";

    case HttpStatus_407_ProxyAuthenticationRequired:
      return "Proxy Authentication Required";

    case HttpStatus_408_RequestTimeout:
      return "Request Timeout";

    case HttpStatus_409_Conflict:
      return "Conflict";

    case HttpStatus_410_Gone:
      return "Gone";

    case HttpStatus_411_LengthRequired:
      return "Length Required";

    case HttpStatus_412_PreconditionFailed:
      return "Precondition Failed";

    case HttpStatus_413_RequestEntityTooLarge:
      return "Request Entity Too Large";

    case HttpStatus_414_RequestUriTooLong:
      return "Request-URI Too Long";

    case HttpStatus_415_UnsupportedMediaType:
      return "Unsupported Media Type";

    case HttpStatus_416_RequestedRangeNotSatisfiable:
      return "Requested Range Not Satisfiable";

    case HttpStatus_417_ExpectationFailed:
      return "Expectation Failed";

    case HttpStatus_422_UnprocessableEntity:
      return "Unprocessable Entity";

    case HttpStatus_423_Locked:
      return "Locked";

    case HttpStatus_424_FailedDependency:
      return "Failed Dependency";

    case HttpStatus_426_UpgradeRequired:
      return "Upgrade Required";

    case HttpStatus_500_InternalServerError:
      return "Internal Server Error";

    case HttpStatus_501_NotImplemented:
      return "Not Implemented";

    case HttpStatus_502_BadGateway:
      return "Bad Gateway";

    case HttpStatus_503_ServiceUnavailable:
      return "Service Unavailable";

    case HttpStatus_504_GatewayTimeout:
      return "Gateway Timeout";

    case HttpStatus_505_HttpVersionNotSupported:
      return "HTTP Version Not Supported";

    case HttpStatus_506_VariantAlsoNegotiates:
      return "Variant Also Negotiates";

    case HttpStatus_507_InsufficientStorage:
      return "Insufficient Storage";

    case HttpStatus_509_BandwidthLimitExceeded:
      return "Bandwidth Limit Exceeded";

    case HttpStatus_510_NotExtended:
      return "Not Extended";

    default:
      throw OrthancException(ErrorCode_ParameterOutOfRange);
    }
  }


  const char* EnumerationToString(ResourceType type)
  {
    switch (type)
    {
      case ResourceType_Patient:
        return "Patient";

      case ResourceType_Study:
        return "Study";

      case ResourceType_Series:
        return "Series";

      case ResourceType_Instance:
        return "Instance";
      
      default:
        throw OrthancException(ErrorCode_ParameterOutOfRange);
    }
  }


  const char* EnumerationToString(ImageFormat format)
  {
    switch (format)
    {
      case ImageFormat_Png:
        return "Png";

      default:
        throw OrthancException(ErrorCode_ParameterOutOfRange);
    }
  }


  const char* EnumerationToString(Encoding encoding)
  {
    switch (encoding)
    {
      case Encoding_Ascii:
        return "Ascii";

      case Encoding_Utf8:
        return "Utf8";

      case Encoding_Latin1:
        return "Latin1";

      case Encoding_Latin2:
        return "Latin2";

      case Encoding_Latin3:
        return "Latin3";

      case Encoding_Latin4:
        return "Latin4";

      case Encoding_Latin5:
        return "Latin5";

      case Encoding_Cyrillic:
        return "Cyrillic";

      case Encoding_Windows1251:
        return "Windows1251";

      case Encoding_Arabic:
        return "Arabic";

      case Encoding_Greek:
        return "Greek";

      case Encoding_Hebrew:
        return "Hebrew";

      case Encoding_Thai:
        return "Thai";

      case Encoding_Japanese:
        return "Japanese";

      case Encoding_Chinese:
        return "Chinese";

      case Encoding_Korean:
        return "Korean";

      case Encoding_JapaneseKanji:
        return "JapaneseKanji";

      case Encoding_SimplifiedChinese:
        return "SimplifiedChinese";

      default:
        throw OrthancException(ErrorCode_ParameterOutOfRange);
    }
  }


  const char* EnumerationToString(PhotometricInterpretation photometric)
  {
    switch (photometric)
    {
      case PhotometricInterpretation_RGB:
        return "RGB";

      case PhotometricInterpretation_Monochrome1:
        return "MONOCHROME1";

      case PhotometricInterpretation_Monochrome2:
        return "MONOCHROME2";

      case PhotometricInterpretation_ARGB:
        return "ARGB";

      case PhotometricInterpretation_CMYK:
        return "CMYK";

      case PhotometricInterpretation_HSV:
        return "HSV";

      case PhotometricInterpretation_Palette:
        return "PALETTE COLOR";

      case PhotometricInterpretation_YBRFull:
        return "YBR_FULL";

      case PhotometricInterpretation_YBRFull422:
        return "YBR_FULL_422";

      case PhotometricInterpretation_YBRPartial420:
        return "YBR_PARTIAL_420"; 

      case PhotometricInterpretation_YBRPartial422:
        return "YBR_PARTIAL_422"; 

      case PhotometricInterpretation_YBR_ICT:
        return "YBR_ICT"; 

      case PhotometricInterpretation_YBR_RCT:
        return "YBR_RCT"; 

      case PhotometricInterpretation_Unknown:
        return "Unknown";

      default:
        throw OrthancException(ErrorCode_ParameterOutOfRange);
    }
  }


  const char* EnumerationToString(RequestOrigin origin)
  {
    switch (origin)
    {
      case RequestOrigin_Unknown:
        return "Unknown";

      case RequestOrigin_DicomProtocol:
        return "DicomProtocol";

      case RequestOrigin_RestApi:
        return "RestApi";

      case RequestOrigin_Plugins:
        return "Plugins";

      case RequestOrigin_Lua:
        return "Lua";

      case RequestOrigin_WebDav:
        return "WebDav";

      default:
        throw OrthancException(ErrorCode_ParameterOutOfRange);
    }
  }


  const char* EnumerationToString(PixelFormat format)
  {
    switch (format)
    {
      case PixelFormat_RGB24:
        return "RGB24";

      case PixelFormat_RGBA32:
        return "RGBA32";

      case PixelFormat_BGRA32:
        return "BGRA32";

      case PixelFormat_Grayscale8:
        return "Grayscale (unsigned 8bpp)";

      case PixelFormat_Grayscale16:
        return "Grayscale (unsigned 16bpp)";

      case PixelFormat_SignedGrayscale16:
        return "Grayscale (signed 16bpp)";

      case PixelFormat_Float32:
        return "Grayscale (float 32bpp)";

      case PixelFormat_Grayscale32:
        return "Grayscale (unsigned 32bpp)";

      case PixelFormat_Grayscale64:
        return "Grayscale (unsigned 64bpp)";

      case PixelFormat_RGB48:
        return "RGB48";

      default:
        throw OrthancException(ErrorCode_ParameterOutOfRange);
    }
  }


  const char* EnumerationToString(ModalityManufacturer manufacturer)
  {
    switch (manufacturer)
    {
      case ModalityManufacturer_Generic:
        return "Generic";

      case ModalityManufacturer_GenericNoWildcardInDates:
        return "GenericNoWildcardInDates";

      case ModalityManufacturer_GenericNoUniversalWildcard:
        return "GenericNoUniversalWildcard";

      case ModalityManufacturer_Vitrea:
        return "Vitrea";
      
      case ModalityManufacturer_GE:
        return "GE";
      
      default:
        throw OrthancException(ErrorCode_ParameterOutOfRange);
    }
  }


  const char* EnumerationToString(DicomRequestType type)
  {
    switch (type)
    {
      case DicomRequestType_Echo:
        return "Echo";
        break;

      case DicomRequestType_Find:
        return "Find";
        break;

      case DicomRequestType_FindWorklist:
        return "FindWorklist";
        break;

      case DicomRequestType_Get:
        return "Get";
        break;

      case DicomRequestType_Move:
        return "Move";
        break;

      case DicomRequestType_Store:
        return "Store";
        break;

      case DicomRequestType_NAction:
        return "N-ACTION";
        break;

      case DicomRequestType_NEventReport:
        return "N-EVENT-REPORT";
        break;

      default: 
        throw OrthancException(ErrorCode_ParameterOutOfRange);
    }
  }


  const char* EnumerationToString(DicomVersion version)
  {
    switch (version)
    {
      case DicomVersion_2008:
        return "2008";

      case DicomVersion_2017c:
        return "2017c";

      case DicomVersion_2021b:
        return "2021b";

      case DicomVersion_2023b:
        return "2023b";

      default: 
        throw OrthancException(ErrorCode_ParameterOutOfRange);
    }
  }


  const char* EnumerationToString(ValueRepresentation vr)
  {
    switch (vr)
    {
      case ValueRepresentation_ApplicationEntity:     // AE
        return "AE";

      case ValueRepresentation_AgeString:             // AS
        return "AS";

      case ValueRepresentation_AttributeTag:          // AT (2 x uint16_t)
        return "AT";

      case ValueRepresentation_CodeString:            // CS
        return "CS";

      case ValueRepresentation_Date:                  // DA
        return "DA";

      case ValueRepresentation_DecimalString:         // DS
        return "DS";

      case ValueRepresentation_DateTime:              // DT
        return "DT";

      case ValueRepresentation_FloatingPointSingle:   // FL (float)
        return "FL";

      case ValueRepresentation_FloatingPointDouble:   // FD (double)
        return "FD";

      case ValueRepresentation_IntegerString:         // IS
        return "IS";

      case ValueRepresentation_LongString:            // LO
        return "LO";

      case ValueRepresentation_LongText:              // LT
        return "LT";

      case ValueRepresentation_OtherByte:             // OB
        return "OB";

      case ValueRepresentation_OtherDouble:           // OD
        return "OD";

      case ValueRepresentation_OtherFloat:            // OF
        return "OF";

      case ValueRepresentation_OtherLong:             // OL
        return "OL";

      case ValueRepresentation_OtherWord:             // OW
        return "OW";

      case ValueRepresentation_PersonName:            // PN
        return "PN";

      case ValueRepresentation_ShortString:           // SH
        return "SH";

      case ValueRepresentation_SignedLong:            // SL (int32_t)
        return "SL";

      case ValueRepresentation_Sequence:              // SQ
        return "SQ";

      case ValueRepresentation_SignedShort:           // SS (int16_t)
        return "SS";

      case ValueRepresentation_ShortText:             // ST
        return "ST";

      case ValueRepresentation_Time:                  // TM
        return "TM";

      case ValueRepresentation_UnlimitedCharacters:   // UC
        return "UC";

      case ValueRepresentation_UniqueIdentifier:      // UI (UID)
        return "UI";

      case ValueRepresentation_UnsignedLong:          // UL (uint32_t)
        return "UL";

      case ValueRepresentation_Unknown:               // UN
        return "UN";

      case ValueRepresentation_UniversalResource:     // UR (URI or URL)
        return "UR";

      case ValueRepresentation_UnsignedShort:         // US (uint16_t)
        return "US";

      case ValueRepresentation_UnlimitedText:         // UT
        return "UT";

      case ValueRepresentation_NotSupported:
        return "Not supported";

      default: 
        throw OrthancException(ErrorCode_ParameterOutOfRange);
    }
  }


  const char* EnumerationToString(JobState state)
  {
    switch (state)
    {
      case JobState_Pending:
        return "Pending";
        
      case JobState_Running:
        return "Running";
        
      case JobState_Success:
        return "Success";
        
      case JobState_Failure:
        return "Failure";
        
      case JobState_Paused:
        return "Paused";
        
      case JobState_Retry:
        return "Retry";
        
      default:
        throw OrthancException(ErrorCode_ParameterOutOfRange);
    }
  }


  const char* EnumerationToString(MimeType mime)
  {
    switch (mime)
    {
      case MimeType_Binary:
        return MIME_BINARY;
        
      case MimeType_Dicom:
        return MIME_DICOM;
        
      case MimeType_Jpeg:
        return MIME_JPEG;
        
      case MimeType_Jpeg2000:
        return MIME_JPEG2000;
        
      case MimeType_Json:
        return MIME_JSON;
        
      case MimeType_Pdf:
        return MIME_PDF;
        
      case MimeType_Png:
        return MIME_PNG;
        
      case MimeType_Xml:
        return MIME_XML;
        
      case MimeType_PlainText:
        return MIME_PLAIN_TEXT;
                
      case MimeType_Pam:
        return MIME_PAM;
                
      case MimeType_Html:
        return MIME_HTML;
                
      case MimeType_Gzip:
        return MIME_GZIP;
                
      case MimeType_JavaScript:
        return MIME_JAVASCRIPT;
                
      case MimeType_Css:
        return MIME_CSS;
                
      case MimeType_WebAssembly:
        return MIME_WEB_ASSEMBLY;
                
      case MimeType_Gif:
        return MIME_GIF;
                
      case MimeType_Zip:
        return MIME_ZIP;
                
      case MimeType_NaCl:
        return MIME_NACL;
                
      case MimeType_PNaCl:
        return MIME_PNACL;
                
      case MimeType_Svg:
        return MIME_SVG;
                
      case MimeType_Woff:
        return MIME_WOFF;

      case MimeType_Woff2:
        return MIME_WOFF2;

      case MimeType_PrometheusText:
        // https://prometheus.io/docs/instrumenting/exposition_formats/#text-based-format
        return "text/plain; version=0.0.4";

      case MimeType_DicomWebJson:
        return MIME_DICOM_WEB_JSON;
                
      case MimeType_DicomWebXml:
        return MIME_DICOM_WEB_XML;

      case MimeType_Ico:
        return MIME_ICO;

      case MimeType_Obj:
        return MIME_OBJ;

      case MimeType_Mtl:
        return MIME_MTL;

      case MimeType_Stl:
        return MIME_STL;

      default:
        throw OrthancException(ErrorCode_ParameterOutOfRange);
    }
  }
  

  const char* EnumerationToString(Endianness endianness)
  {
    switch (endianness)
    {
      case Endianness_Little:
        return "Little-endian";

      case Endianness_Big:
        return "Big-endian";

      case Endianness_Unknown:
        return "Unknown endianness";
                
      default:
        throw OrthancException(ErrorCode_ParameterOutOfRange);
    }
  }


  const char* EnumerationToString(StorageCommitmentFailureReason reason)
  {
    switch (reason)
    {
      case StorageCommitmentFailureReason_Success:
        return "Success";

      case StorageCommitmentFailureReason_ProcessingFailure:
        return "A general failure in processing the operation was encountered";

      case StorageCommitmentFailureReason_NoSuchObjectInstance:
        return "One or more of the elements in the Referenced SOP "
          "Instance Sequence was not available";
        
      case StorageCommitmentFailureReason_ResourceLimitation:
        return "The SCP does not currently have enough resources to "
          "store the requested SOP Instance(s)";

      case StorageCommitmentFailureReason_ReferencedSOPClassNotSupported:
        return "Storage Commitment has been requested for a SOP Instance "
          "with a SOP Class that is not supported by the SCP";

      case StorageCommitmentFailureReason_ClassInstanceConflict:
        return "The SOP Class of an element in the Referenced SOP Instance Sequence "
          "did not correspond to the SOP class registered for this SOP Instance at the SCP";

      case StorageCommitmentFailureReason_DuplicateTransactionUID:
        return "The Transaction UID of the Storage Commitment Request is already in use";

      default:
        return "Unknown failure reason";
    }
  }


  const char* EnumerationToString(DicomToJsonFormat format)
  {
    switch (format)
    {
      case DicomToJsonFormat_Full:
        return "Full";

      case DicomToJsonFormat_Human:
        return "Simplify";

      case DicomToJsonFormat_Short:
        return "Short";

      default:
        throw OrthancException(ErrorCode_ParameterOutOfRange);
    }
  }


  Encoding StringToEncoding(const char* encoding)
  {
    std::string s(encoding);
    Toolbox::ToUpperCase(s);

    if (s == "UTF8")
    {
      return Encoding_Utf8;
    }

    if (s == "ASCII")
    {
      return Encoding_Ascii;
    }

    if (s == "LATIN1")
    {
      return Encoding_Latin1;
    }

    if (s == "LATIN2")
    {
      return Encoding_Latin2;
    }

    if (s == "LATIN3")
    {
      return Encoding_Latin3;
    }

    if (s == "LATIN4")
    {
      return Encoding_Latin4;
    }

    if (s == "LATIN5")
    {
      return Encoding_Latin5;
    }

    if (s == "CYRILLIC")
    {
      return Encoding_Cyrillic;
    }

    if (s == "WINDOWS1251")
    {
      return Encoding_Windows1251;
    }

    if (s == "ARABIC")
    {
      return Encoding_Arabic;
    }

    if (s == "GREEK")
    {
      return Encoding_Greek;
    }

    if (s == "HEBREW")
    {
      return Encoding_Hebrew;
    }

    if (s == "THAI")
    {
      return Encoding_Thai;
    }

    if (s == "JAPANESE")
    {
      return Encoding_Japanese;
    }

    if (s == "CHINESE")
    {
      return Encoding_Chinese;
    }

    if (s == "KOREAN")
    {
      return Encoding_Korean;
    }

    if (s == "JAPANESEKANJI")
    {
      return Encoding_JapaneseKanji;
    }

    if (s == "SIMPLIFIEDCHINESE")
    {
      return Encoding_SimplifiedChinese;
    }

    throw OrthancException(ErrorCode_ParameterOutOfRange);
  }


  ResourceType StringToResourceType(const char* type)
  {
    std::string s(type);
    Toolbox::ToUpperCase(s);

    if (s == "PATIENT" || s == "PATIENTS")
    {
      return ResourceType_Patient;
    }
    else if (s == "STUDY" || s == "STUDIES")
    {
      return ResourceType_Study;
    }
    else if (s == "SERIES")
    {
      return ResourceType_Series;
    }
    else if (s == "INSTANCE"  || s == "IMAGE" || 
             s == "INSTANCES" || s == "IMAGES")
    {
      return ResourceType_Instance;
    }

    throw OrthancException(ErrorCode_ParameterOutOfRange, std::string("Invalid resource type '") + type + "'");
  }

  const char* ResourceTypeToDicomQueryRetrieveLevel(ResourceType type)
  {
    if (type == ResourceType_Patient)
    {
      return "PATIENT";
    }
    else if (type == ResourceType_Study)
    {
      return "STUDY";
    }
    else if (type == ResourceType_Series)
    {
      return "SERIES";
    }
    else if (type == ResourceType_Instance)
    {
      return "IMAGE";
    }

    throw OrthancException(ErrorCode_ParameterOutOfRange);
  }

  ImageFormat StringToImageFormat(const char* format)
  {
    std::string s(format);
    Toolbox::ToUpperCase(s);

    if (s == "PNG")
    {
      return ImageFormat_Png;
    }

    throw OrthancException(ErrorCode_ParameterOutOfRange);
  }


  ValueRepresentation StringToValueRepresentation(const std::string& vr,
                                                  bool throwIfUnsupported)
  {
    if (vr == "AE")
    {
      return ValueRepresentation_ApplicationEntity;
    }
    else if (vr == "AS")
    {
      return ValueRepresentation_AgeString;
    }
    else if (vr == "AT")
    {
      return ValueRepresentation_AttributeTag;
    }
    else if (vr == "CS")
    {
      return ValueRepresentation_CodeString;
    }
    else if (vr == "DA")
    {
      return ValueRepresentation_Date;
    }
    else if (vr == "DS")
    {
      return ValueRepresentation_DecimalString;
    }
    else if (vr == "DT")
    {
      return ValueRepresentation_DateTime;
    }
    else if (vr == "FL")
    {
      return ValueRepresentation_FloatingPointSingle;
    }
    else if (vr == "FD")
    {
      return ValueRepresentation_FloatingPointDouble;
    }
    else if (vr == "IS")
    {
      return ValueRepresentation_IntegerString;
    }
    else if (vr == "LO")
    {
      return ValueRepresentation_LongString;
    }
    else if (vr == "LT")
    {
      return ValueRepresentation_LongText;
    }
    else if (vr == "OB")
    {
      return ValueRepresentation_OtherByte;
    }
    else if (vr == "OD")
    {
      return ValueRepresentation_OtherDouble;
    }
    else if (vr == "OF")
    {
      return ValueRepresentation_OtherFloat;
    }
    else if (vr == "OL")
    {
      return ValueRepresentation_OtherLong;
    }
    else if (vr == "OW")
    {
      return ValueRepresentation_OtherWord;
    }
    else if (vr == "PN")
    {
      return ValueRepresentation_PersonName;
    }
    else if (vr == "SH")
    {
      return ValueRepresentation_ShortString;
    }
    else if (vr == "SL")
    {
      return ValueRepresentation_SignedLong;
    }
    else if (vr == "SQ")
    {
      return ValueRepresentation_Sequence;
    }
    else if (vr == "SS")
    {
      return ValueRepresentation_SignedShort;
    }
    else if (vr == "ST")
    {
      return ValueRepresentation_ShortText;
    }
    else if (vr == "TM")
    {
      return ValueRepresentation_Time;
    }
    else if (vr == "UC")
    {
      return ValueRepresentation_UnlimitedCharacters;
    }
    else if (vr == "UI")
    {
      return ValueRepresentation_UniqueIdentifier;
    }
    else if (vr == "UL")
    {
      return ValueRepresentation_UnsignedLong;
    }
    else if (vr == "UN")
    {
      return ValueRepresentation_Unknown;
    }
    else if (vr == "UR")
    {
      return ValueRepresentation_UniversalResource;
    }
    else if (vr == "US")
    {
      return ValueRepresentation_UnsignedShort;
    }
    else if (vr == "UT")
    {
      return ValueRepresentation_UnlimitedText;
    }
    else
    {
      std::string s = "Unsupported value representation encountered: " + vr;

      if (throwIfUnsupported)
      {
        throw OrthancException(ErrorCode_ParameterOutOfRange, s);
      }
      else
      {
        LOG(INFO) << s;
        return ValueRepresentation_NotSupported;
      }
    }
  }


  PhotometricInterpretation StringToPhotometricInterpretation(const char* value)
  {
    // http://dicom.nema.org/medical/dicom/2017a/output/chtml/part03/sect_C.7.6.3.html#sect_C.7.6.3.1.2
    std::string s(value);

    if (s == "MONOCHROME1")
    {
      return PhotometricInterpretation_Monochrome1;
    }
    
    if (s == "MONOCHROME2")
    {
      return PhotometricInterpretation_Monochrome2;
    }

    if (s == "PALETTE COLOR")
    {
      return PhotometricInterpretation_Palette;
    }
    
    if (s == "RGB")
    {
      return PhotometricInterpretation_RGB;
    }
    
    if (s == "HSV")
    {
      return PhotometricInterpretation_HSV;
    }
    
    if (s == "ARGB")
    {
      return PhotometricInterpretation_ARGB;
    }    

    if (s == "CMYK")
    {
      return PhotometricInterpretation_CMYK;
    }    

    if (s == "YBR_FULL")
    {
      return PhotometricInterpretation_YBRFull;
    }
    
    if (s == "YBR_FULL_422")
    {
      return PhotometricInterpretation_YBRFull422;
    }
    
    if (s == "YBR_PARTIAL_422")
    {
      return PhotometricInterpretation_YBRPartial422;
    }
    
    if (s == "YBR_PARTIAL_420")
    {
      return PhotometricInterpretation_YBRPartial420;
    }
    
    if (s == "YBR_ICT")
    {
      return PhotometricInterpretation_YBR_ICT;
    }
    
    if (s == "YBR_RCT")
    {
      return PhotometricInterpretation_YBR_RCT;
    }

    throw OrthancException(ErrorCode_ParameterOutOfRange);
  }
  

  ModalityManufacturer StringToModalityManufacturer(const std::string& manufacturer)
  {
    ModalityManufacturer result;
    bool obsolete = false;
    
    if (manufacturer == "Generic")
    {
      return ModalityManufacturer_Generic;
    }
    else if (manufacturer == "GenericNoWildcardInDates")
    {
      return ModalityManufacturer_GenericNoWildcardInDates;
    }
    else if (manufacturer == "GenericNoUniversalWildcard")
    {
      return ModalityManufacturer_GenericNoUniversalWildcard;
    }
    else if (manufacturer == "Vitrea")
    {
      return ModalityManufacturer_Vitrea;
    }
    else if (manufacturer == "GE")
    {
      return ModalityManufacturer_GE;
    }
    else if (manufacturer == "AgfaImpax" ||
             manufacturer == "SyngoVia")
    {
      result = ModalityManufacturer_GenericNoWildcardInDates;
      obsolete = true;
    }
    else if (manufacturer == "EFilm2" ||
             manufacturer == "MedInria" ||
             manufacturer == "ClearCanvas" ||
             manufacturer == "Dcm4Chee"
             )
    {
      result = ModalityManufacturer_Generic;
      obsolete = true;
    }
    else
    {
      throw OrthancException(ErrorCode_ParameterOutOfRange,
                             "Unknown modality manufacturer: \"" + manufacturer + "\"");
    }

    if (obsolete)
    {
      LOG(WARNING) << "The \"" << manufacturer << "\" manufacturer is now obsolete. "
                   << "To guarantee compatibility with future Orthanc "
                   << "releases, you should replace it by \""
                   << EnumerationToString(result)
                   << "\" in your configuration file.";
    }

    return result;
  }


  DicomVersion StringToDicomVersion(const std::string& version)
  {
    if (version == "2008")
    {
      return DicomVersion_2008;
    }
    else if (version == "2017c")
    {
      return DicomVersion_2017c;
    }
    else if (version == "2021b")
    {
      return DicomVersion_2021b;
    }
    else if (version == "2023b")
    {
      return DicomVersion_2023b;
    }
    else
    {
      throw OrthancException(ErrorCode_ParameterOutOfRange,
                             "Unknown specific version of the DICOM standard: " + version);
    }
  }


  JobState StringToJobState(const std::string& state)
  {
    if (state == "Pending")
    {
      return JobState_Pending;
    }
    else if (state == "Running")
    {
      return JobState_Running;
    }
    else if (state == "Success")
    {
      return JobState_Success;
    }
    else if (state == "Failure")
    {
      return JobState_Failure;
    }
    else if (state == "Paused")
    {
      return JobState_Paused;
    }
    else if (state == "Retry")
    {
      return JobState_Retry;
    }
    else
    {
      throw OrthancException(ErrorCode_ParameterOutOfRange);
    }
  }


  RequestOrigin StringToRequestOrigin(const std::string& origin)
  {
    if (origin == "Unknown")
    {
      return RequestOrigin_Unknown;
    }
    else if (origin == "DicomProtocol")
    {
      return RequestOrigin_DicomProtocol;
    }
    else if (origin == "RestApi")
    {
      return RequestOrigin_RestApi;
    }
    else if (origin == "Plugins")
    {
      return RequestOrigin_Plugins;
    }
    else if (origin == "Lua")
    {
      return RequestOrigin_Lua;
    }
    else if (origin == "WebDav")
    {
      return RequestOrigin_WebDav;
    }
    else
    {
      throw OrthancException(ErrorCode_ParameterOutOfRange);
    }
  }


  bool LookupMimeType(MimeType& target,
                      const std::string& source)
  {
    if (source == MIME_BINARY)
    {
      target = MimeType_Binary;
      return true;
    }
    else if (source == MIME_DICOM)
    {
      target = MimeType_Dicom;
      return true;
    }
    else if (source == MIME_JPEG ||
             source == "image/jpg")
    {
      // Note the tolerance for "image/jpg", which is *not* a standard MIME type
      // https://groups.google.com/g/orthanc-users/c/Y5x37UFKiDg/m/1zI260KTAwAJ
      // https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Image_types
      target = MimeType_Jpeg;
      return true;
    }
    else if (source == MIME_JPEG2000)
    {
      target = MimeType_Jpeg2000;
      return true;
    }
    else if (source == MIME_JSON)
    {
      target = MimeType_Json;
      return true;
    }
    else if (source == MIME_PDF)
    {
      target = MimeType_Pdf;
      return true;
    }
    else if (source == MIME_PNG)
    {
      target = MimeType_Png;
      return true;
    }
    else if (source == MIME_XML ||
             source == MIME_XML_2)
    {
      target = MimeType_Xml;
      return true;
    }
    else if (source == MIME_PLAIN_TEXT)
    {
      target = MimeType_PlainText;
      return true;
    }
    else if (source == MIME_PAM)
    {
      target = MimeType_Pam;
      return true;
    }
    else if (source == MIME_HTML)
    {
      target = MimeType_Html;
      return true;
    }
    else if (source == MIME_GZIP)
    {
      target = MimeType_Gzip;
      return true;
    }
    else if (source == MIME_JAVASCRIPT)
    {
      target = MimeType_JavaScript;
      return true;
    }
    else if (source == MIME_CSS)
    {
      target = MimeType_Css;
      return true;
    }
    else if (source == MIME_WEB_ASSEMBLY)
    {
      target = MimeType_WebAssembly;
      return true;
    }
    else if (source == MIME_GIF)
    {
      target = MimeType_Gif;
      return true;
    }
    else if (source == MIME_ZIP)
    {
      target = MimeType_Zip;
      return true;
    }
    else if (source == MIME_NACL)
    {
      target = MimeType_NaCl;
      return true;
    }
    else if (source == MIME_PNACL)
    {
      target = MimeType_PNaCl;
      return true;
    }
    else if (source == MIME_SVG)
    {
      target = MimeType_Svg;
      return true;
    }
    else if (source == MIME_WOFF)
    {
      target = MimeType_Woff;
      return true;
    }
    else if (source == MIME_WOFF2)
    {
      target = MimeType_Woff2;
      return true;
    }
    else if (source == MIME_DICOM_WEB_JSON)
    {
      target = MimeType_DicomWebJson;
      return true;
    }
    else if (source == MIME_DICOM_WEB_XML)
    {
      target = MimeType_DicomWebXml;
      return true;
    }
    else if (source == MIME_ICO)
    {
      target = MimeType_Ico;
      return true;
    }
    else if (source == MIME_OBJ)
    {
      target = MimeType_Obj;
      return true;
    }
    else if (source == MIME_MTL)
    {
      target = MimeType_Mtl;
      return true;
    }
    else if (source == MIME_STL)
    {
      target = MimeType_Stl;
      return true;
    }
    else
    {
      return false;
    }
  }


  
  MimeType StringToMimeType(const std::string& mime)
  {
    MimeType result;
    if (LookupMimeType(result, mime))
    {
      return result;
    }
    else
    {
      throw OrthancException(ErrorCode_ParameterOutOfRange);
    }
  }
  

  DicomToJsonFormat StringToDicomToJsonFormat(const std::string& format)
  {
    if (format == "Full")
    {
      return DicomToJsonFormat_Full;
    }
    else if (format == "Short")
    {
      return DicomToJsonFormat_Short;
    }
    else if (format == "Simplify")
    {
      return DicomToJsonFormat_Human;
    }
    else
    {
      throw OrthancException(ErrorCode_ParameterOutOfRange);
    }
  }


  unsigned int GetBytesPerPixel(PixelFormat format)
  {
    switch (format)
    {
      case PixelFormat_Grayscale8:
        return 1;

      case PixelFormat_Grayscale16:
      case PixelFormat_SignedGrayscale16:
        return 2;

      case PixelFormat_RGB24:
        return 3;

      case PixelFormat_RGBA32:
      case PixelFormat_BGRA32:
      case PixelFormat_Grayscale32:
        return 4;

      case PixelFormat_Float32:
        assert(sizeof(float) == 4);
        return 4;

      case PixelFormat_RGB48:
        return 6;

      case PixelFormat_Grayscale64:
      case PixelFormat_RGBA64:
        return 8;

      default:
        throw OrthancException(ErrorCode_ParameterOutOfRange);
    }
  }


  bool GetDicomEncoding(Encoding& encoding,
                        const char* specificCharacterSet)
  {
    std::string s = Toolbox::StripSpaces(specificCharacterSet);
    Toolbox::ToUpperCase(s);

    // handle common spelling mistakes
    boost::replace_all(s, "ISO_IR_", "ISO_IR ");
    boost::replace_all(s, "ISO_2022_IR_", "ISO 2022 IR ");


    // http://dicom.nema.org/medical/dicom/current/output/html/part03.html#sect_C.12.1.1.2
    // https://github.com/dcm4che/dcm4che/blob/master/dcm4che-core/src/main/java/org/dcm4che3/data/SpecificCharacterSet.java
    if (s == "ISO_IR 6" ||
        s == "ISO 2022 IR 6")
    {
      encoding = Encoding_Ascii;
    }
    else if (s == "ISO_IR 192")
    {
      encoding = Encoding_Utf8;
    }
    else if (s == "ISO_IR 100" ||
             s == "ISO 2022 IR 100")
    {
      encoding = Encoding_Latin1;
    }
    else if (s == "ISO_IR 101" ||
             s == "ISO 2022 IR 101")
    {
      encoding = Encoding_Latin2;
    }
    else if (s == "ISO_IR 109" ||
             s == "ISO 2022 IR 109")
    {
      encoding = Encoding_Latin3;
    }
    else if (s == "ISO_IR 110" ||
             s == "ISO 2022 IR 110")
    {
      encoding = Encoding_Latin4;
    }
    else if (s == "ISO_IR 148" ||
             s == "ISO 2022 IR 148")
    {
      encoding = Encoding_Latin5;
    }
    else if (s == "ISO_IR 144" ||
             s == "ISO 2022 IR 144")
    {
      encoding = Encoding_Cyrillic;
    }
    else if (s == "ISO_IR 127" ||
             s == "ISO 2022 IR 127")
    {
      encoding = Encoding_Arabic;
    }
    else if (s == "ISO_IR 126" ||
             s == "ISO 2022 IR 126")
    {
      encoding = Encoding_Greek;
    }
    else if (s == "ISO_IR 138" ||
             s == "ISO 2022 IR 138")
    {
      encoding = Encoding_Hebrew;
    }
    else if (s == "ISO_IR 166" ||
             s == "ISO 2022 IR 166")
    {
      encoding = Encoding_Thai;
    }
    else if (s == "ISO_IR 13" ||
             s == "ISO 2022 IR 13")
    {
      encoding = Encoding_Japanese;
    }
    else if (s == "GB18030" || s == "GBK")
    {
      /**
       * According to tumashu@163.com, "In China, many dicom file's
       * 0008,0005 tag is set as "GBK", instead of "GB18030", GBK is a
       * subset of GB18030, and which is used frequently in China,
       * suggest support it."
       * https://groups.google.com/d/msg/orthanc-users/WMM8LMbjpUc/02-1f_yFCgAJ
       **/
      encoding = Encoding_Chinese;
    }
    else if (s == "ISO 2022 IR 149")
    {
      encoding = Encoding_Korean;
    }
    else if (s == "ISO 2022 IR 87")
    {
      encoding = Encoding_JapaneseKanji;
    }
    else if (s == "ISO 2022 IR 58")
    {
      encoding = Encoding_SimplifiedChinese;
    }
    /*
      else if (s == "ISO 2022 IR 159")
      {
      TODO - Supplementary Kanji set
      }
    */
    else
    {
      return false;
    }

    // The encoding was properly detected
    return true;
  }


  ResourceType GetChildResourceType(ResourceType type)
  {
    switch (type)
    {
      case ResourceType_Patient:
        return ResourceType_Study;

      case ResourceType_Study:
        return ResourceType_Series;
        
      case ResourceType_Series:
        return ResourceType_Instance;

      default:
        throw OrthancException(ErrorCode_ParameterOutOfRange);
    }
  }


  ResourceType GetParentResourceType(ResourceType type)
  {
    switch (type)
    {
      case ResourceType_Study:
        return ResourceType_Patient;
        
      case ResourceType_Series:
        return ResourceType_Study;

      case ResourceType_Instance:
        return ResourceType_Series;

      default:
        throw OrthancException(ErrorCode_ParameterOutOfRange);
    }
  }


  bool IsResourceLevelAboveOrEqual(ResourceType level,
                                   ResourceType reference)
  {
    switch (reference)
    {
      case ResourceType_Patient:
        return (level == ResourceType_Patient);

      case ResourceType_Study:
        return (level == ResourceType_Patient ||
                level == ResourceType_Study);

      case ResourceType_Series:
        return (level == ResourceType_Patient ||
                level == ResourceType_Study ||
                level == ResourceType_Series);

      case ResourceType_Instance:
        return (level == ResourceType_Patient ||
                level == ResourceType_Study ||
                level == ResourceType_Series ||
                level == ResourceType_Instance);

      default:
        throw OrthancException(ErrorCode_ParameterOutOfRange);
    }
  }


  DicomModule GetModule(ResourceType type)
  {
    switch (type)
    {
      case ResourceType_Patient:
        return DicomModule_Patient;

      case ResourceType_Study:
        return DicomModule_Study;
        
      case ResourceType_Series:
        return DicomModule_Series;

      default:
        throw OrthancException(ErrorCode_ParameterOutOfRange);
    }
  }



  const char* GetDicomSpecificCharacterSet(Encoding encoding)
  {
    // http://dicom.nema.org/medical/dicom/current/output/html/part03.html#sect_C.12.1.1.2
    switch (encoding)
    {
      case Encoding_Ascii:
        return "ISO_IR 6";

      case Encoding_Utf8:
        return "ISO_IR 192";

      case Encoding_Latin1:
        return "ISO_IR 100";

      case Encoding_Latin2:
        return "ISO_IR 101";

      case Encoding_Latin3:
        return "ISO_IR 109";

      case Encoding_Latin4:
        return "ISO_IR 110";

      case Encoding_Latin5:
        return "ISO_IR 148";

      case Encoding_Cyrillic:
        return "ISO_IR 144";

      case Encoding_Arabic:
        return "ISO_IR 127";

      case Encoding_Greek:
        return "ISO_IR 126";

      case Encoding_Hebrew:
        return "ISO_IR 138";

      case Encoding_Japanese:
        return "ISO_IR 13";

      case Encoding_Chinese:
        return "GB18030";

      case Encoding_Thai:
        return "ISO_IR 166";

      case Encoding_Korean:
        return "ISO 2022 IR 149";

      case Encoding_JapaneseKanji:
        return "ISO 2022 IR 87";

      case Encoding_SimplifiedChinese:
        return "ISO 2022 IR 58";

      default:
        throw OrthancException(ErrorCode_ParameterOutOfRange);
    }
  }


  // This function is autogenerated by the script
  // "Resources/CodeGeneration/GenerateErrorCodes.py"
  HttpStatus ConvertErrorCodeToHttpStatus(ErrorCode error)
  {
    switch (error)
    {
      case ErrorCode_Success:
        return HttpStatus_200_Ok;

      case ErrorCode_ParameterOutOfRange:
        return HttpStatus_400_BadRequest;

      case ErrorCode_BadParameterType:
        return HttpStatus_400_BadRequest;

      case ErrorCode_InexistentItem:
        return HttpStatus_404_NotFound;

      case ErrorCode_BadRequest:
        return HttpStatus_400_BadRequest;

      case ErrorCode_UriSyntax:
        return HttpStatus_400_BadRequest;

      case ErrorCode_InexistentFile:
        return HttpStatus_404_NotFound;

      case ErrorCode_BadFileFormat:
        return HttpStatus_400_BadRequest;

      case ErrorCode_UnknownResource:
        return HttpStatus_404_NotFound;

      case ErrorCode_InexistentTag:
        return HttpStatus_404_NotFound;

      case ErrorCode_BadJson:
        return HttpStatus_400_BadRequest;

      case ErrorCode_Unauthorized:
        return HttpStatus_401_Unauthorized;

      case ErrorCode_NotAcceptable:
        return HttpStatus_406_NotAcceptable;

      case ErrorCode_DatabaseUnavailable:
        return HttpStatus_503_ServiceUnavailable;

      case ErrorCode_BadRange:
        return HttpStatus_416_RequestedRangeNotSatisfiable;

      case ErrorCode_DatabaseCannotSerialize:
        return HttpStatus_503_ServiceUnavailable;

      case ErrorCode_Revision:
        return HttpStatus_409_Conflict;

      case ErrorCode_ForbiddenAccess:
        return HttpStatus_403_Forbidden;

      case ErrorCode_DuplicateResource:
        return HttpStatus_409_Conflict;

      case ErrorCode_CreateDicomNotString:
        return HttpStatus_400_BadRequest;

      case ErrorCode_CreateDicomOverrideTag:
        return HttpStatus_400_BadRequest;

      case ErrorCode_CreateDicomUseContent:
        return HttpStatus_400_BadRequest;

      case ErrorCode_CreateDicomNoPayload:
        return HttpStatus_400_BadRequest;

      case ErrorCode_CreateDicomUseDataUriScheme:
        return HttpStatus_400_BadRequest;

      case ErrorCode_CreateDicomBadParent:
        return HttpStatus_400_BadRequest;

      case ErrorCode_CreateDicomParentIsInstance:
        return HttpStatus_400_BadRequest;

      case ErrorCode_UnsupportedMediaType:
        return HttpStatus_415_UnsupportedMediaType;

      default:
        return HttpStatus_500_InternalServerError;
    }
  }


  bool IsUserContentType(FileContentType type)
  {
    return (type >= FileContentType_StartUser &&
            type <= FileContentType_EndUser);
  }


  bool IsBinaryValueRepresentation(ValueRepresentation vr)
  {
    // http://dicom.nema.org/medical/dicom/current/output/chtml/part05/sect_6.2.html

    switch (vr)
    {
      case ValueRepresentation_ApplicationEntity:     // AE
      case ValueRepresentation_AgeString:             // AS
      case ValueRepresentation_CodeString:            // CS
      case ValueRepresentation_Date:                  // DA
      case ValueRepresentation_DecimalString:         // DS
      case ValueRepresentation_DateTime:              // DT
      case ValueRepresentation_IntegerString:         // IS
      case ValueRepresentation_LongString:            // LO
      case ValueRepresentation_LongText:              // LT
      case ValueRepresentation_PersonName:            // PN
      case ValueRepresentation_ShortString:           // SH
      case ValueRepresentation_ShortText:             // ST
      case ValueRepresentation_Time:                  // TM
      case ValueRepresentation_UnlimitedCharacters:   // UC
      case ValueRepresentation_UniqueIdentifier:      // UI (UID)
      case ValueRepresentation_UniversalResource:     // UR (URI or URL)
      case ValueRepresentation_UnlimitedText:         // UT
      {
        return false;
      }

      /**
       * Below are all the VR whose character repertoire is tagged as
       * "not applicable"
       **/
      case ValueRepresentation_AttributeTag:          // AT (2 x uint16_t)
      case ValueRepresentation_FloatingPointSingle:   // FL (float)
      case ValueRepresentation_FloatingPointDouble:   // FD (double)
      case ValueRepresentation_OtherByte:             // OB
      case ValueRepresentation_OtherDouble:           // OD
      case ValueRepresentation_OtherFloat:            // OF
      case ValueRepresentation_OtherLong:             // OL
      case ValueRepresentation_OtherWord:             // OW
      case ValueRepresentation_SignedLong:            // SL (int32_t)
      case ValueRepresentation_Sequence:              // SQ
      case ValueRepresentation_SignedShort:           // SS (int16_t)
      case ValueRepresentation_UnsignedLong:          // UL (uint32_t)
      case ValueRepresentation_Unknown:               // UN
      case ValueRepresentation_UnsignedShort:         // US (uint16_t)
      {
        return true;
      }

      case ValueRepresentation_NotSupported:
      default:
        throw OrthancException(ErrorCode_ParameterOutOfRange);
    }
  }  


  static Mutex     defaultEncodingMutex_;  // Should not be necessary
  static Encoding  defaultEncoding_ = ORTHANC_DEFAULT_DICOM_ENCODING;
  
  Encoding GetDefaultDicomEncoding()
  {
    Mutex::ScopedLock lock(defaultEncodingMutex_);
    return defaultEncoding_;
  }

  void SetDefaultDicomEncoding(Encoding encoding)
  {
    std::string name = EnumerationToString(encoding);
    
    {
      Mutex::ScopedLock lock(defaultEncodingMutex_);
      defaultEncoding_ = encoding;
    }

    LOG(INFO) << "Default encoding for DICOM was changed to: " << name;
  }


  const char* GetResourceTypeText(ResourceType type,
                                  bool isPlural,
                                  bool isUpperCase)
  {
    if (isPlural && !isUpperCase)
    {
      switch (type)
      {
        case ResourceType_Patient:
          return "patients";

        case ResourceType_Study:
          return "studies";

        case ResourceType_Series:
          return "series";

        case ResourceType_Instance:
          return "instances";
      
        default:
          throw OrthancException(ErrorCode_ParameterOutOfRange);
      }
    }
    else if (isPlural && isUpperCase)
    {
      switch (type)
      {
        case ResourceType_Patient:
          return "Patients";

        case ResourceType_Study:
          return "Studies";

        case ResourceType_Series:
          return "Series";

        case ResourceType_Instance:
          return "Instances";
      
        default:
          throw OrthancException(ErrorCode_ParameterOutOfRange);
      }
    }
    else if (!isPlural && !isUpperCase)
    {
      switch (type)
      {
        case ResourceType_Patient:
          return "patient";

        case ResourceType_Study:
          return "study";

        case ResourceType_Series:
          return "series";

        case ResourceType_Instance:
          return "instance";
      
        default:
          throw OrthancException(ErrorCode_ParameterOutOfRange);
      }
    }
    else if (!isPlural && isUpperCase)
    {
      switch (type)
      {
        case ResourceType_Patient:
          return "Patient";

        case ResourceType_Study:
          return "Study";

        case ResourceType_Series:
          return "Series";

        case ResourceType_Instance:
          return "Instance";
      
        default:
          throw OrthancException(ErrorCode_ParameterOutOfRange);
      }
    }
    else
    {
      throw OrthancException(ErrorCode_InternalError);
    }
  }

  DicomTransferSyntax GetTransferSyntax(const std::string& value)
  {
    DicomTransferSyntax syntax;
    if (LookupTransferSyntax(syntax, value))
    {
      return syntax;
    }
    else
    {
      throw OrthancException(ErrorCode_ParameterOutOfRange,
                             "Unknown transfer syntax: " + value);
    }
  }

}


#include "./Enumerations_TransferSyntaxes.impl.h"
